package com.spring;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class HwfApplicationContext {
    private Class configClass;

    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap<>();  //单例池，存放单例对象
    private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();  //存放扫描到的所有Bean的定义
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();  //存放所有的的beanPostProcessor

    public HwfApplicationContext(Class configClass) {
        this.configClass = configClass;
        //解析接收到的配置类
        //解析ComponentScan注解 ---> 拿到扫描路径  ---->扫描  ---> BeanDefinition ---->BeanDefinitionMap
        scan(configClass);

        for (Map.Entry<String,BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if(beanDefinition.getScope().equals("singleton")){
                Object bean = createBean(beanName,beanDefinition); //创建单例bean
                singletonObjects.put(beanName,bean);
            }

        }
    }

    public Object createBean(String beanName,BeanDefinition beanDefinition){
        Class clazz = beanDefinition.getClazz();
        try {
            //创建bean
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //依赖注入，给该对象中的属性赋值
            for (Field declaredField : clazz.getDeclaredFields()) {
                if(declaredField.isAnnotationPresent(Autowired.class)){
                    //根据什么赋值呢？byType or byName?
                    Object bean = getBean(declaredField.getName());
                    declaredField.setAccessible(true); //放开权限
                    declaredField.set(instance,bean);
                }
            }

            //Aware回调
            if(instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            //在初始化之前调用相应的beanPostProcessor的before逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
            }
            //初始化
            if(instance instanceof InitializingBean){
                ((InitializingBean) instance).afterPropertiesSet();
            }
            //在初始化之后调用相应的beanPostProcessor的after逻辑
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
            }
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private void scan(Class configClass) {
        ComponentScan componentScan = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);
        String path = componentScan.value();//扫描路径
        path = path.replace(".","/");

        //扫描,判断路径下的哪些类加了相应的注解
        ClassLoader classLoader = HwfApplicationContext.class.getClassLoader(); //app应用类加载器
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                String fileName = f.getAbsolutePath();
                if(!fileName.endsWith(".class"))
                    continue;
                String className = fileName.substring(fileName.indexOf("com"),fileName.indexOf(".class"));
                className = className.replace("\\",".");

                Class<?> clazz = null;
                try {
                    clazz = classLoader.loadClass(className);
                    if(clazz.isAnnotationPresent(Component.class)){
                        //当前这个类是一个Bean
                        //解析类，判断当前bean是单例bean还是prototype的bean --->BeanDefinition

                        //如果当前类是一个BeanPostProcessor,就直接实例化存入list中
                        if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                            BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                            beanPostProcessorList.add(instance);
                        }

                        Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                        String beanName = componentAnnotation.value();

                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setClazz(clazz);
                        if(clazz.isAnnotationPresent(Scope.class)){
                            Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                            beanDefinition.setScope(scopeAnnotation.value());
                        } else{
                            beanDefinition.setScope("singleton");
                        }
                        beanDefinitionMap.put(beanName,beanDefinition);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }


            }
        }
    }

    public Object getBean(String name){
        if(beanDefinitionMap.containsKey(name)){
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if(beanDefinition.getScope().equals("singleton")){
                Object o = singletonObjects.get(name);//从单例池中去拿
                return o;
            }else {
                //不是单例的，每次都需要创建bean对象
                Object bean = createBean(name,beanDefinition);
                return bean;
            }
        }else {
            //不存在这个bean
            throw new RuntimeException("没有这个bean");
        }

    }
}
